package com.linkhub.oms.admin.modules.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.linkhub.oms.admin.common.exception.ExceptionFactory;
import com.linkhub.oms.admin.modules.order.domain.dto.OrderFulfillmentExpenseDTO;
import com.linkhub.oms.admin.modules.order.service.OrderExpenseBizService;
import com.linkhub.oms.admin.modules.order.service.OrderInfoAssembleService;
import com.linkhub.oms.admin.modules.satoken.session.LhSessionContext;
import com.linkhub.oms.admin.modules.user.account.domain.dto.UserAccountDTO;
import com.linkhub.oms.admin.modules.user.account.service.IUserAccountService;
import com.linkhub.oms.admin.modules.user.accountflow.service.IUserAccountFlowBizService;
import com.linkhub.oms.common.enums.mp.BoolEnum;
import com.linkhub.oms.common.enums.mp.DataStatusEnum;
import com.linkhub.oms.common.enums.mp.DocumentTypeEnum;
import com.linkhub.oms.common.enums.mp.FreezeStatusEnum;
import com.linkhub.oms.common.enums.mp.TransactionTypeEnum;
import com.linkhub.oms.common.util.QtBigDecimalUtil;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.dao.mpservice.ICompanyService;
import com.linkhub.oms.dao.mpservice.ICountryTaxService;
import com.linkhub.oms.dao.mpservice.ILocalProductService;
import com.linkhub.oms.dao.mpservice.IOrderAddressService;
import com.linkhub.oms.dao.mpservice.IOrderService;
import com.linkhub.oms.entity.CompanyEntity;
import com.linkhub.oms.entity.CountryTaxEntity;
import com.linkhub.oms.entity.LocalProductEntity;
import com.linkhub.oms.entity.OrderAddressEntity;
import com.linkhub.oms.entity.OrderEntity;
import com.linkhub.oms.entity.UserAccountFlowEntity;
import com.linkhub.oms.entity.resulttype.order.OrderInfoDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * @Description: OrderExpenseBizService
 * @Author: echo
 * @Date: 2024/6/2 10:50
 * @Version: 1.0
 */
@Slf4j
@Service
public class OrderExpenseBizServiceImpl implements OrderExpenseBizService {

    @Resource
    private OrderInfoAssembleService orderInfoAssembleService;
    @Resource
    private ILocalProductService localProductService;
    @Resource
    private IUserAccountFlowBizService userAccountFlowService;
    @Resource
    private IUserAccountService userAccountService;
    @Resource
    private IOrderService orderService;
    @Resource
    private IOrderAddressService orderAddressService;
    @Resource
    private ICountryTaxService countryTaxService;
    @Resource
    private ICompanyService companyService;

    public static final BigDecimal OPERATING_OF_A = new BigDecimal("0.60");
    public static final BigDecimal OPERATING_OF_B = new BigDecimal("0.85");
    public static final BigDecimal OPERATING_OF_C = new BigDecimal("0.10");

    public static final BigDecimal ONE_HUNDRED = new BigDecimal("100");

    @Override
    public BigDecimal operatingCharge(String sysOrderId) {
        OrderInfoDO orderInfoDO = orderInfoAssembleService.assembleOrder(sysOrderId);
        List<String> localSkuIds = orderInfoDO.getLocalSkuIds();
        if (CollUtil.isEmpty(localSkuIds)) {
            return BigDecimal.ZERO;
        }

        int productQuantity = orderInfoDO.acquireOrderProductQuantity();
        if (productQuantity > 1) {
            // b + (n-1) * c
            BigDecimal multiClassResult = NumberUtil.add(OPERATING_OF_B, NumberUtil.mul(productQuantity - 1, OPERATING_OF_C));
            return QtBigDecimalUtil.scale2(multiClassResult);
        }

        List<LocalProductEntity> products = localProductService.listBySkuIds(localSkuIds);
        if (CollUtil.isEmpty(products)) {
            throw ExceptionFactory.getI18nBizException("order.product.delete.count.error");
        }

        LocalProductEntity product = products.get(0);
        boolean readyShip = BoolEnum.YES.getCode().equals(product.getReadyShip());
        return readyShip ? OPERATING_OF_A : OPERATING_OF_B;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void thawOrderExpense(String sysOrderId) {
        List<UserAccountFlowEntity> accountFlows = userAccountFlowService.queryAccountFlow(sysOrderId,
                TransactionTypeEnum.FREEZE,
                DocumentTypeEnum.ORDER);

        if (CollUtil.isEmpty(accountFlows)) {
            return;
        }
        String userId = accountFlows.get(0).getUserId();

        // accountFlows 更新是否解冻
        List<UserAccountFlowEntity> updateUnfreezeStatusList = QtStreamUtil.map(accountFlows, flow -> {
            UserAccountFlowEntity entity = new UserAccountFlowEntity();
            entity.setId(flow.getId());
            entity.setUnfreezeStatus(FreezeStatusEnum.UN_FREEZE);
            return entity;
        });

        userAccountFlowService.updateBatchById(updateUnfreezeStatusList);

        // 更新冻结金额
        BigDecimal freezeBalance = QtStreamUtil.sum(accountFlows, UserAccountFlowEntity::getAmount);
        UserAccountDTO accountDTO = userAccountService.updateAccountAmount(userId, freezeBalance, TransactionTypeEnum.UN_FREEZE);

        // 转换成解冻 实体
        List<UserAccountFlowEntity> unfreezeFlows = QtStreamUtil.map(accountFlows, flow -> {
            UserAccountFlowEntity entity = new UserAccountFlowEntity();
            entity.setUserId(flow.getUserId());
            entity.setTransactionId(IdUtil.getSnowflake().nextIdStr());
            entity.setTransactionType(TransactionTypeEnum.UN_FREEZE);
            entity.setUnfreezeStatus(FreezeStatusEnum.FREEZE);
            entity.setDocumentId(flow.getDocumentId());
            entity.setDocumentType(DocumentTypeEnum.ORDER);
            entity.setExpenseType(flow.getExpenseType());

            entity.setAmount(flow.getAmount());
            entity.setRemainingBalance(accountDTO.getTotalBalance());
            entity.setDataStatus(DataStatusEnum.ENABLE);

            entity.setCreatedTime(LocalDateTime.now());
            entity.setUpdateTime(LocalDateTime.now());

            return entity;
        });

        userAccountFlowService.saveBatch(unfreezeFlows);


    }

    @Override
    public OrderFulfillmentExpenseDTO calculateVat(String sysOrderId) {
        OrderFulfillmentExpenseDTO expenseDTO = new OrderFulfillmentExpenseDTO();
        expenseDTO.setVat(BigDecimal.ZERO);
        expenseDTO.setHandlingFee(BigDecimal.ZERO);

        OrderEntity order = orderService.getByOrderId(sysOrderId);
        if(order==null || order.getCurrentTotalPrice()==null){
            return expenseDTO;
        }

        BigDecimal totalPrice = new BigDecimal(order.getCurrentTotalPrice());
        OrderAddressEntity orderAddress = orderAddressService.getByOrderIds(sysOrderId);
        String countryCode = orderAddress.getCountryCode();
        CountryTaxEntity countryTax = countryTaxService.getByCountryCode(countryCode);
        // 国家关税表没有维护该国vat税率信息
        if(countryTax==null){
            return expenseDTO;
        }
        String userid = LhSessionContext.getUserId();
        List<CompanyEntity> companys = companyService.listByUserId(userid);
        String ioss = null;
        if(CollUtil.isNotEmpty(companys)){
            ioss = companys.get(0).getIoss();
        }
        // 国家属于欧盟，但未填ioss
        if(BoolEnum.YES.equals(countryTax.getIsEu()) && ioss == null){
            return expenseDTO;
        }

        // 订单总金额<国家起征点
        if(QtBigDecimalUtil.isSmaller(totalPrice, countryTax.getTaxThreshold())){
            return expenseDTO;
        }

        BigDecimal vat = NumberUtil.mul(totalPrice,NumberUtil.div(countryTax.getVatRate(),ONE_HUNDRED));
        BigDecimal handlingFee = NumberUtil.mul(totalPrice,NumberUtil.div(countryTax.getHandlingFeeRate(),ONE_HUNDRED));

        expenseDTO.setVat(QtBigDecimalUtil.scale2(vat));
        expenseDTO.setHandlingFee(QtBigDecimalUtil.scale2(handlingFee));
        return expenseDTO;
    }

    @Override
    public List<OrderFulfillmentExpenseDTO> calculateVat(List<String> sysOrderIds) {
        return QtStreamUtil.map(sysOrderIds, this::calculateVat);
    }

}
